#include "MusicEngine.h"
#include "UI/SPlayerWindow.h"
#include <Common/Network.h>
#include "NetworkMusicStream.h"
#include "Common/Common.h"
#include "NetworkEngine/NetworkFileManager.h"
#include "MusicSound.h"
#include "MusicFileFactory.h"

using namespace SPlayer;
using namespace SPlayer::Common;
using namespace SPlayer::Music;

void Music::MusicEngine::onSoundAutoStopped(IrrKlang::ISound^ sound, IrrKlang::StopEventCause reason, System::Object^ userData)
{
    // check if current playing music is this sound?
    if (_irrSound != sound)
        return;
    _irrSound = nullptr;
    isNetworkPlaying = false;

    Function::InvokeSync(eventDispatcher->onFinished);
}

Music::MusicEngine::MusicEngine()
{
    _irrEngine = gcnew IrrKlang::ISoundEngine;
}

bool Music::MusicEngine::init()
{
    musicEngine = gcnew MusicEngine;
    musicEngine->_irrEngine->AddFileFactory(gcnew MusicFileFactory);
    return musicEngine != nullptr;
}

void Music::MusicEngine::stop()
{
    auto irrsound = _irrSound;
    _irrSound = nullptr;
    if (irrsound)
    {
        irrsound->setSoundStopEventReceiver(nullptr);
        irrsound->Stop();

        Function::InvokeSync(eventDispatcher->onStoped);
        isNetworkPlaying = false;
    }
}

bool Music::MusicEngine::pause()
{
    if (hasMusic && _irrSound->Paused == false)
    {
        _irrSound->Paused = true;
        eventDispatcher->onPaused();
        return true;
    }
    return false;
}

bool Music::MusicEngine::continuePlay()
{
    if (hasMusic && _irrSound->Paused == true)
    {
        _irrSound->Paused = false;
        Function::InvokeSync(eventDispatcher->onContinued);
    }
    return true;
}

bool Music::MusicEngine::playLocalFile(System::String ^filename)
{
    System::Diagnostics::Debug::Assert(filename != nullptr);
    System::Diagnostics::Debug::Assert(filename->Length != 0);

    if (IO::FileInfo(filename).Exists == false)
        return false;

    // first, stop the music playing.
    stop();

    // then, play new music.
    _irrSound = _irrEngine->Play2D(filename);

    if (_irrSound == nullptr)
        return false;

    _irrSound->setSoundStopEventReceiver(this);

    Function::InvokeSync(eventDispatcher->onPlayed, filename);
    return true;
}

bool SPlayer::Music::MusicEngine::playNetworkFile(System::String ^networkFileName)
{
    System::Diagnostics::Debug::Assert(networkFileName != nullptr);
    System::Diagnostics::Debug::Assert(networkFileName->Length != 0);

    // first, stop the music playing.
    stop();

    // then, play new music.
    return preparePlayNetworkFile(networkFileName);

}

bool SPlayer::Music::MusicEngine::playCachedFile(System::String ^networkFileName)
{
    System::Diagnostics::Debug::Assert(networkFileName != nullptr);
    System::Diagnostics::Debug::Assert(networkFileName->Length != 0);

    auto filename = MusicFileFactory::toLocalPath(networkFileName);

    if (IO::FileInfo(filename).Exists == false)
        return false;

    // first, stop the music playing.
    stop();

    // then, play new music.
    _irrSound = _irrEngine->Play2D(filename);

    if (_irrSound == nullptr)
        return false;

    _irrSound->setSoundStopEventReceiver(this);

    Function::InvokeSync(eventDispatcher->onPlayed, networkFileName);
    return true;
}


bool SPlayer::Music::MusicEngine::preparePlayNetworkFile(System::String ^networkFileName)
{
    //if (MusicFileFactory::isCached(networkFileName))
    //{
    //    return playCachedFile(networkFileName);
    //}

    this->networkMusicFile = NetworkFileManager<NetworkMusicFile>::globalManager->getFile(networkFileName->Substring(10));
    networkMusicFile->seek(0, IO::SeekOrigin::Begin);
    networkMusicFile->ensureOffset(0);

    this->networkLyricFile = NetworkFileManager<NetworkLyricFile>::globalManager->getFile(networkFileName->Substring(10));
    this->networkLyricFile->startDownload();

    NetworkFileReadableTracker ^traceHelper = gcnew NetworkFileReadableTracker(networkMusicFile);
    traceHelper->traceReadable(0, 1024*30, gcnew Action(this, &MusicEngine::onNetworkFilePrepared),
                               10000, nullptr);
    return true;
}

void SPlayer::Music::MusicEngine::onNetworkFilePrepared()
{
    // first, stop the music playing.
    stop();

    auto networkFileName = "SPlayer://" + networkMusicFile->musicTitle;
    // TODO : change "SPlayer://" into some other format.
    _irrSound = _irrEngine->Play2D(networkFileName, false, true, IrrKlang::StreamMode::Streaming);
    
    if (_irrSound == nullptr)
        return;

    _irrSound->setSoundStopEventReceiver(this);

    isNetworkPlaying = true;
    Function::InvokeSync(eventDispatcher->onPlayed, networkFileName);
}

bool Music::MusicEngine::hasMusic::get()
{
    return _irrSound != nullptr;
}

unsigned int Music::MusicEngine::length::get()
{
    if (_irrSound == nullptr)
        return 1;
    return _irrSound->PlayLength;
}

unsigned Music::MusicEngine::playOffset::get()
{
    if (_irrSound == nullptr)
        return 0;
    return _irrSound->PlayPosition;
}

void Music::MusicEngine::playOffset::set(unsigned offset)
{
    if (_irrSound == nullptr)
        return;
    if (isNetworkPlaying && !this->networkMusicFile->downloadFinished)
        return;
    _irrSound->PlayPosition = offset;
}

float Music::MusicEngine::volume::get()
{
    return _irrSound->Volume;
}

void SPlayer::Music::MusicEngine::volume::set(float volume)
{
    if (_irrSound == nullptr)
        return;
    _irrSound->Volume = volume;
}
